package yl.hs.bmipfull.service.report.impl;

import com.alibaba.fastjson2.JSONException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import yl.hs.bmipfull.daos.laboratory.IProductOverageDao;
import yl.hs.bmipfull.daos.material.IPurchaseReceiptDao;
import yl.hs.bmipfull.daos.material.IRawMaterialInventoryDao;
import yl.hs.bmipfull.daos.material.IWarehouseDao;
import yl.hs.bmipfull.daos.production.*;
import yl.hs.bmipfull.daos.report.*;
import yl.hs.bmipfull.pojo.laboratory.ProductOverageEntity;
import yl.hs.bmipfull.pojo.material.PurchaseReceiptEntity;
import yl.hs.bmipfull.pojo.production.ProductShipOrderEntity;
import yl.hs.bmipfull.pojo.production.ProductionLineEntity;
import yl.hs.bmipfull.pojo.production.ProductionLinePortEntity;
import yl.hs.bmipfull.pojo.production.ProductionRecordDetailsEntity;
import yl.hs.bmipfull.pojo.report.*;
import yl.hs.bmipfull.service.production.impl.ProductionBaseService;
import yl.hs.bmipfull.service.report.IProductionDayReportService;
import yl.hs.bmipfull.utils.HsApp;
import yl.hs.bmipfull.utils.Tl;
import yl.hs.bmipfull.utils.datastate.ActionType;
import yl.hs.bmipfull.utils.datastate.SurplusState;
import yl.hs.bmipfull.viewmodels.PageQuery;
import yl.hs.bmipfull.viewmodels.PageResult;
import yl.hs.bmipfull.viewmodels.SubmitModel;
import yl.hs.bmipfull.viewmodels.base.StringDecimalModel;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/*
 * ProductionDayReportServiceImpl
 * @category
 * @author 谢景广
 * @date 2023-02-02
 */
@Slf4j
@Service
public class ProductionDayReportServiceImpl extends ProductionBaseService implements IProductionDayReportService {

    @Override
    public Integer generateReport(Date day, String unit, String cateSN) {
        ProductionDayReportEntity report = new ProductionDayReportEntity();
        report.setContractUnit(unit);
        report.setDay(day);
        Calendar cal = Calendar.getInstance();
        List<ProductionDayReportMaterialEntity> lastReportMaterials = new ArrayList<>();
        List<ProductionDayReportLineOutputEntity> lastOutputList = new ArrayList<>();
        if (!Tl.getDateString(day, "yyyy-MM-dd").endsWith("01")) {
            var lastReport = reportDao.queryLastReport(unit, cateSN, day);
            if (lastReport != null) {
                report.setMonthOutput(lastReport.getMonthOutput());
                report.setMonthlyReturnQuantity(lastReport.getMonthlyReturnQuantity());
                report.setMonthlyIntake(lastReport.getMonthlyIntake());
                lastReportMaterials.addAll(materialDao.queryByReportSN(lastReport.getSn()));
                lastOutputList.addAll(outputDao.queryByReportSN(lastReport.getSn()));
            }
        }
        var startTime = Tl.strToDate(Tl.getDateString(day, "yyyy-MM-dd 08:00"));
        report.setSn(queryNewSN(report.getClass()));
        cal.setTime(startTime);
        cal.add(Calendar.DATE, 1);
        Date endTime = cal.getTime();
        report.setStartTime(startTime);
        report.setEndTime(endTime);
        var orderList = orderDao.queryListByTimeAndUnit(unit, cateSN, startTime, endTime);
        var taskSNGroup = orderList.stream().collect(Collectors.groupingBy(ProductShipOrderEntity::getTaskSN, Collectors.toList()));
        for (var taskSN : taskSNGroup.keySet()) {
            var task = new ProductionDayReportTaskEntity();
            var orders = taskSNGroup.get(taskSN).stream().sorted(Comparator.comparing(ProductShipOrderEntity::getOutTime)).toList();
            task.setSn(taskSN);
            var order = orders.get(0);
            task.setBuildPosition(order.getBuildPosition());
            task.setReportSN(report.getSn());
            task.setCustomerName(order.getCustomerName());
            task.setProductName(order.getProjectName());
            task.setProductName(order.getProductName());
            task.setPumpWay(readPumpWay(order.getProductTags()));
            task.setProjectName(order.getProjectName());
            task.setQuantity(orders.stream().map(ProductShipOrderEntity::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add));
            reportTaskDao.insertNew(task);
            var sac = BigDecimal.ZERO;
            for (var o : orders) {
                var ro = new ProductionDayReportOrderEntity();
                ro.setSn(o.getSn());
                ro.setTaskSN(o.getTaskSN());
                ro.setReportSN(report.getSn());
                ro.setOutTime(o.getOutTime());
                ro.setQuantity(o.getQuantity());
                ro.setDriverName(o.getDriverName());
                ro.setTruckCode(o.getTruckCode());
                ro.setPlName(o.getProductLineName());
                sac = sac.add(o.getQuantity());
                ro.setAccumulative(sac);
                reportOrderDao.insertNew(ro);
            }
        }
        var dayQuantity = orderList.stream().map(ProductShipOrderEntity::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        report.setDayOutput(dayQuantity);
        var monthOut = orderDao.queryMonthQuantity(unit, cateSN, Tl.strToDate(Tl.getDateString(day, "yyyy-MM-01 08:00")), endTime);

        report.setMonthOutput(monthOut == null ? BigDecimal.ZERO : monthOut);
        var dayOverageList = overageDao.queryListByByTimeAndUnit(unit, cateSN, startTime, endTime);
        var dayOverage = dayOverageList.stream().map(ProductOverageEntity::getOverageWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
        report.setDayReturnQuantity(dayOverage);
        var scrap = dayOverageList.stream().filter(t -> (t.getHandlerType() == null ? 30 : t.getHandlerType()) == SurplusState.Scrap.code).map(ProductOverageEntity::getOverageWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
        report.setScrapQuantity(scrap);
        report.setTransReturnQuantity(dayOverage.subtract(scrap));
        report.setMonthlyReturnQuantity(report.getMonthlyReturnQuantity().add(dayOverage));
        var linePortList = portDao.queryListByUnit2(unit);
        var whSNList = linePortList.stream().map(ProductionLinePortEntity::getWhSN).filter(whSN -> !Tl.isEmpty(whSN)).toList();
        var inventoryList = inventoryDao.queryListByUnit(unit);
        var whouse = warehouseDao.queryAll();
        List<PurchaseReceiptEntity> receiptList = new ArrayList<>();
        if (whSNList.size() > 0) {
            receiptList.addAll(receiptDao.queryListByWhSNAndTime(startTime, endTime, whSNList));
        }
        var consumeList = consumeDao.queryListByTimeAndUnit(unit, startTime, endTime);
        var lineList = lineDao.getAll();
        var unitLines = lineList.stream().filter(t -> t.getUnit().equals(unit)).toList();
        var dayRecordList = productionRecordDao.queryLinesDataByTime(unitLines.stream().map(ProductionLineEntity::getSn).toList(), startTime, endTime);
        for (ProductionLinePortEntity item : linePortList) {
            var material = new ProductionDayReportMaterialEntity();
            var lastM = lastReportMaterials.stream().filter(t -> t.getPortCode().equals(t.getPortCode()) && t.getPlSN().equals(item.getLineSN()) && t.getWarehouseSn().equals(item.getWhSN())).findFirst();
            BigDecimal monthlyExpend = BigDecimal.ZERO, monthlyInQuantity = BigDecimal.ZERO;
            if (lastM.isPresent()) {
                monthlyExpend = lastM.get().getMonthlyExpend();
                monthlyInQuantity = lastM.get().getMonthlyInQuantity();
            }
            material.setSn(queryNewSN(material.getClass()));
            material.setPortCode(item.getPortCode());
            material.setPortName(item.getPortName());
            var mDayIn = receiptList.stream().filter(t -> t.getWarehouseSN().equals(item.getWhSN())).map(PurchaseReceiptEntity::getNetWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
            var lineRecordIdList = dayRecordList.stream().filter(t -> t.getProductLineSn().equals(item.getLineSN())).map(t -> t.getId()).toList();
            var mDayConsume = consumeList.stream().filter(t -> lineRecordIdList.contains(t.getProductionRecordId()) && t.getWarehouseSn().equals(item.getWhSN())).map(ProductionRecordDetailsEntity::getConsumptionQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
            material.setDayInQuantity(mDayIn);
            material.setMonthlyInQuantity((monthlyInQuantity.add(mDayIn)));
            material.setDayExpend(mDayConsume.divide(BigDecimal.valueOf(1000),3, RoundingMode.HALF_UP));
            material.setMonthlyExpend((monthlyExpend.add(mDayConsume.divide(BigDecimal.valueOf(1000),3,RoundingMode.HALF_UP))));
            material.setPlSN(item.getLineSN());
            var wares = whouse.stream().filter(t -> t.getSn().equals(item.getWhSN())).findFirst();
            wares.ifPresent(WarehouseEntity -> material.setStockQuantity(WarehouseEntity.getInventory()));
            material.setReportSN(report.getSn());
            material.setWarehouseSn(item.getWhSN());
            materialDao.insertNew(material);
        }
             /*   }
            }
        }*/
        report.setProductCateSN(cateSN);


        if (unitLines.size() > 0) {

            var unitLineSnList = unitLines.stream().map(ProductionLineEntity::getSn).toList();
            List<StringDecimalModel> lineMonthData = productionRecordDao.queryLinesMonthOutputSummary(unitLineSnList, Tl.strToDate(Tl.getDateString(day, "yyyy-MM-01 08:00")), endTime);
            List<StringDecimalModel> lineShipMonthData = orderDao.queryLineShipQuantity(unitLineSnList, Tl.strToDate(Tl.getDateString(day, "yyyy-MM-01 08:00")), endTime);
            for (var line : unitLines) {
                var item = new ProductionDayReportLineOutputEntity();
                item.setDay(Tl.getDateString(day, "yyyy-MM-dd"));
                item.setReportSn(report.getSn());
                item.setDayOutput(dayRecordList.stream().filter(t -> t.getProductLineSn().equals(line.getSn())).map(t -> Tl.getBigDecimal(t.getActualOutput())).reduce(BigDecimal.ZERO, BigDecimal::add));
                var lastOpt = lineMonthData.stream().filter(t -> t.getSn().equals(line.getSn())).findFirst();
                if (lastOpt.isPresent()) {
                    item.setMonthlyOutput(lastOpt.get().getValue());
                } else {
                    item.setMonthlyOutput(item.getDayOutput());
                }
                var query = orderList.stream().filter(t -> t.getLineSN().equals(line.getSn())).map(ProductShipOrderEntity::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
                item.setShipDayQuantity(orderList.stream().filter(t -> t.getLineSN().equals(line.getSn())).map(ProductShipOrderEntity::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add));
                var lineMonthShipQuantityOpt = lineShipMonthData.stream().filter(t -> t.getSn().equals(line.getSn())).findFirst();
                if (lineMonthShipQuantityOpt.isPresent()) {
                    item.setShipMonthlyQuantity(lineMonthShipQuantityOpt.get().getValue());
                } else {
                    item.setShipMonthlyQuantity(item.getShipDayQuantity());
                }
                item.setLineSN(line.getSn());
                item.setLineName(line.getPlName());
                outputDao.insertNew(item);
            }
        }
        return reportDao.insertNew(report);
    }

    private String readPumpWay(String tag) {
        var value = "";
        var st = tag.split(";");
        for (var t : st) {
            if (t.contains("productBuildWay")) {
                value = t.split(":")[1].replace(",", "");
                break;
            }
        }
        return value;
    }

    //@Scheduled(cron = "10 40 8 * * ?")
    @Override
    public void autoGenerateReport() {
        var lineList = lineDao.getAll();
        var s = lineList.stream().collect(Collectors.groupingBy(ProductionLineEntity::getUnit, Collectors.groupingBy(ProductionLineEntity::getUseRang)));
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DATE, -1);
        var format = new SimpleDateFormat("yyyy-MM-dd");
        var todayStr = format.format(cal.getTime());
        for (var unit : s.keySet()) {
            var cateGroup = s.get(unit);
            for (var cateSN : cateGroup.keySet()) {
                try {
                    generateReport(format.parse(todayStr), unit, cateSN);
                } catch (Exception ee) {
                    log.error("生成生产日报时发生错误", ee);
                }
            }
        }
    }

    @Override
    public void autoGenerateReport(Date time) {
        var lineList = lineDao.getAll();
        var s = lineList.stream().collect(Collectors.groupingBy(ProductionLineEntity::getUnit, Collectors.groupingBy(ProductionLineEntity::getUseRang)));
        Calendar cal = Calendar.getInstance();
        cal.setTime(time);
        cal.add(Calendar.DATE, -1);
        var format = new SimpleDateFormat("yyyy-MM-dd");
        var todayStr = format.format(cal.getTime());
        for (var unit : s.keySet()) {
            var cateGroup = s.get(unit);
            for (var cateSN : cateGroup.keySet()) {
                try {
                    generateReport(format.parse(todayStr), unit, cateSN);
                } catch (Exception ee) {
                    log.error("生成生产日报时发生错误", ee);
                }
            }
        }
    }

    @Override
    public Map<String,Object> productCostByDay(Date time, String unit) {
        Map<String,Object> result=new HashMap<>();
        List<Map<String,Object>> dataList=new ArrayList<>();
        List<Map<String,Object>> productList=consumeDao.findProductNameGroupByTime(time,unit);
        List<String> columnList=new ArrayList<>();
        columnList.add("强度等级");
        columnList.addAll(consumeDao.findMaterialsNameGroupByTime(time,unit));
        columnList.add("该标号日方量(m³)");
        columnList.add("该标号每方混凝土成本（元）");
        columnList.add("该标号日混凝土成本（元）");
        result.put("columnList",columnList);
        var list=consumeDao.findSumConsumptionQuantityGroupByTime(time, unit);
        for (var productMap: productList) {
            Map<String,Object> resultMap=new HashMap<>();
            var productName=productMap.get("productName").toString();
            var actualOutput=BigDecimal.valueOf(Double.parseDouble(productMap.get("actualOutput").toString()));
            BigDecimal sumAmount=BigDecimal.ZERO;
            for (var column:columnList) {
                switch (column) {
                    case "强度等级" -> resultMap.put(column, productName);
                    case "该标号日方量(m³)" -> resultMap.put(column, actualOutput);
                    case "该标号每方混凝土成本（元）" -> {
                        if(sumAmount.compareTo(BigDecimal.ZERO)==0 || actualOutput.compareTo(BigDecimal.ZERO)==0){
                            resultMap.put(column, 0);
                        }else
                        {
                            resultMap.put(column, sumAmount.divide(actualOutput,2,RoundingMode.HALF_UP));
                        }

                    }
                    case "该标号日混凝土成本（元）" -> resultMap.put(column, sumAmount.setScale(2,RoundingMode.HALF_UP));
                    default -> {
                        var str = list.stream().filter(t -> t.get("productName").equals(productName) && t.get("rmName").equals(column)).toList();
                        if (str != null && str.size() > 0) {
                            resultMap.put(column, str.get(0).get("consumptionQuantity"));
                            sumAmount = sumAmount.add(BigDecimal.valueOf(Double.parseDouble(str.get(0).get("amount")==null?"0":str.get(0).get("amount").toString())));
                        } else {
                            resultMap.put(column, 0);
                        }
                    }
                }
            }
            dataList.add(resultMap);
        }
        result.put("dataList",dataList);
        return result;
    }

    @Override
    public PageResult<ProductionDayReportEntity> queryList(PageQuery pageQuery, int rowNum, int page) {
        var result = new PageResult<ProductionDayReportEntity>(rowNum);
        var query = pageQuery.getQuery();
        query.put("sortSide", pageQuery.getSortSides());
        pageQuery.checkSortSides(ProductionDayReportEntity.class);
        result.setRecords(reportDao.queryPageCount(query));
        result.setPage(page);
        query.put("querySkip", (page - 1) * rowNum);
        query.put("pageSize", rowNum);
        query.put("sortSide", String.join(",", pageQuery.getSortSides()));
        result.setData(reportDao.queryList(query));
        return result;
    }

    @Override
    public List<ProductionDayReportTaskEntity> queryReportTasks(String sn) {
        return reportTaskDao.queryByReportSn(sn);
    }

    @Override
    public List<Map<String, Object>> queryReportMaterialsCombination(String sn) {
        var mList = materialDao.queryByReportSN(sn);
        var report = reportDao.findBySN(sn);
        var lines = lineDao.getAll();
        if (report == null) {
            throw new RuntimeException("没有找到编号为：" + sn + "的报表信息");
        }
        List<Map<String, Object>> data = new ArrayList<>();
        var linePortList = portDao.queryListByUnit(report.getContractUnit());
        var pGroup = linePortList.stream().collect(Collectors.groupingBy(ProductionLinePortEntity::getLineSN, Collectors.groupingBy(ProductionLinePortEntity::getPortCode,
                Collectors.groupingBy(ProductionLinePortEntity::getPortName,
                        Collectors.toList()))));
        var ngs = pGroup.keySet().stream().sorted().toList();
        for (ProductionLinePortEntity items : linePortList) {
            var nameList = mList.stream().filter(t -> t.getWarehouseSn().equals(items.getWhSN())).toList();
            Map<String, Object> item = new HashMap<>();
            item.put("名称", items.getPortName());
            item.put("代码", items.getPortCode());
            var dayInQuantity = nameList.get(0).getDayInQuantity();
            item.put("日入库(吨)", dayInQuantity);
            var monthlyInQuantity = nameList.get(0).getMonthlyInQuantity();
            item.put("月入库(吨)", monthlyInQuantity);
            List<String> liness = mList.stream().map(t -> t.getPlSN()).distinct().toList();
            for (String s : liness) {
                var lineOpt = lines.stream().filter(t -> t.getSn().equals(s)).findFirst();
                var days = nameList.stream().filter(t -> t.getPlSN().equals(s)).findFirst();
                var months = nameList.stream().filter(t -> t.getPlSN().equals(s)).findFirst();
                BigDecimal day = BigDecimal.valueOf(0);
                BigDecimal month = BigDecimal.valueOf(0);
                if (days.isPresent()) {
                    day = days.get().getDayExpend();
                }
                if (months.isPresent()) {
                    month = months.get().getMonthlyExpend();
                }

                item.put(lineOpt.get().getPlName() + "," + "日消耗(吨)", day);
                item.put(lineOpt.get().getPlName() + "," + "月消耗(吨)", month);
            }
            var store = nameList.get(0).getStockQuantity();
            if(items.getPortName().equals("M1水")||items.getPortName().equals("M2水")){
                item.put("库存(吨)", 0);
            }else{
                item.put("库存(吨)",store);
            }
            data.add(item);

        }
            /*}
        }*/
        return data.stream().sorted(Comparator.comparing(map -> map.get("代码").toString())).toList();
    }

    @Override
    public List<ProductionDayReportMaterialEntity> queryReportMaterialsList(String sn) {
        return materialDao.queryByReportSN(sn);
    }

    @Override
    public List<ProductionDayReportLineOutputEntity> queryReportLines(String sn) {
        return outputDao.queryByReportSN(sn);
    }

    @Override
    public List<ProductionDayReportOrderEntity> queryReportOrders(String sn, String taskSN) {
        return reportOrderDao.queryByReportSnAndTaskSN(sn, taskSN);
    }

    @Override
    public Integer addMaterialInfo(SubmitModel model) throws Exception {
        ProductionDayReportMaterialEntity entity;
        if (Tl.isEmpty(model.getSn())) {

            if (Tl.isEmpty(Tl.getString(model.getData().get("reportSN")))) {
                throw new JSONException("reportSN 不能为空");
            }
            entity = new ProductionDayReportMaterialEntity();
            entity.setSn(queryNewSN(entity.getClass()));
            Tl.getChangeLog(entity, model.getData());
            materialDao.deleteByCodeAndLine(entity.getPortCode(), entity.getPlSN(), entity.getReportSN());
            var result = materialDao.insertNew(entity);
            if (result > 0) {
                var report = reportDao.findBySN(entity.getReportSN());
                writeLog(ActionType.CREATE, entity.getSn(), "增加到" + report.getDay() + "编号为：" + report.getSn() + "中", null, report.getClass());
            }
            return result;
        }
        return 0;
    }

    @Override
    public Integer saveReportInfo(SubmitModel model) throws Exception {
        if (!Tl.isEmpty(model.getSn())) {
            var report = reportDao.findBySN(model.getSn());
            var backJson = report.toJson();
            var log = Tl.getChangeLog(report, model.getData());
            var user = HsApp.getCurrent();
            assert user != null;
            report.setOrganizeCode(user.getOrganizationCode());
            var result = reportDao.update(report);
            if (result > 0) {
                writeLog(ActionType.UPDATE, report.getSn(), log, backJson, report.getClass());
            }
            return result;
        }
        return 0;
    }

    @Autowired
    public void setReportDao(IProductionDayReportDao reportDao) {
        this.reportDao = reportDao;
    }

    @Autowired
    public void setMaterialDao(IProductionDayReportMaterialDao materialDao) {
        this.materialDao = materialDao;
    }

    @Autowired
    public void setReportTaskDao(IProductionDayReportTaskDao reportTaskDao) {
        this.reportTaskDao = reportTaskDao;
    }

    @Autowired
    public void setReportOrderDao(IProductionDayReportOrderDao reportOrderDao) {
        this.reportOrderDao = reportOrderDao;
    }

    @Autowired
    public void setLineDao(IProductionLineDao lineDao) {
        this.lineDao = lineDao;
    }

    @Autowired
    public void setOrderDao(IProductShipOrderDao orderDao) {
        this.orderDao = orderDao;
    }

    @Autowired
    public void setOverageDao(IProductOverageDao overageDao) {
        this.overageDao = overageDao;
    }

    @Autowired
    public void setPortDao(IProductionLinePortDao portDao) {
        this.portDao = portDao;
    }

    @Autowired
    public void setReceiptDao(IPurchaseReceiptDao receiptDao) {
        this.receiptDao = receiptDao;
    }

    @Autowired
    public void setConsumeDao(IProductionRecordDetailsDao consumeDao) {
        this.consumeDao = consumeDao;
    }

    @Autowired
    public void setInventoryDao(IRawMaterialInventoryDao inventoryDao) {
        this.inventoryDao = inventoryDao;
    }

    @Autowired
    public void setOutputDao(IProductionDayReportLineOutputDao outputDao) {
        this.outputDao = outputDao;
    }

    @Autowired
    public void setProductionRecordDao(IProductionRecordDao productionRecordDao) {
        this.productionRecordDao = productionRecordDao;
    }

    @Autowired
    private IWarehouseDao warehouseDao;
    private IProductionDayReportDao reportDao;
    private IProductionDayReportTaskDao reportTaskDao;
    private IProductionDayReportOrderDao reportOrderDao;
    private IProductionDayReportMaterialDao materialDao;
    private IProductionLineDao lineDao;
    private IProductShipOrderDao orderDao;
    private IProductOverageDao overageDao;
    private IProductionLinePortDao portDao;
    private IPurchaseReceiptDao receiptDao;
    private IProductionRecordDetailsDao consumeDao;
    private IRawMaterialInventoryDao inventoryDao;
    private IProductionDayReportLineOutputDao outputDao;
    private IProductionRecordDao productionRecordDao;
}
